#ifndef COMMON_RABBITMQ_H
#define COMMON_RABBITMQ_H

#include <amqpcpp.h>
#include <amqpcpp/libev.h>
#include <ev.h>
#include <openssl/opensslv.h>
#include <openssl/ssl.h>

#include <functional>
#include <memory>
#include <string>
#include <thread>

namespace chat {
/**
 * @note 封装思想：
        - 将事件监控结构 loop，通信连接 connection，以及信道 channel 封装起来
        - 提供创建交换机，队列并进行直接绑定的接口
        - 提供向指定交换机发布消息的接口
        - 提供订阅指定队列消息的接口
 */
class MQClient {
   public:
    using MessageCallback_t = std::function<void(const char*, size_t)>;

    /**
     * @param user[in]: 客户端用户
     * @param password[in]: 用户密码
     * @param host[in]: rabbitmq服务主机地址，形如: 127.0.0.1:5672
     */
    MQClient(const std::string& user, const std::string& password,
             const std::string& host);

    ~MQClient();

    /**
     * @brief 创建交换机，队列并进行直接绑定
     * @param exchange_name[in]: 交换机名字
     * @param queue_name[in]: 队列名字
     * @param exchange_type[in]: 交换机类型 - AMQP::ExchangeType::direct
     * @param routing_key[in]: 路由键
     */
    void declareCompoents(const std::string& exchange_name,
                          const std::string& queue_name,
                          const AMQP::ExchangeType exchange_type,
                          const std::string& routing_key);

    /**
     * @brief 客户端向交换机推送消息
     * @param exchange_name[in]: 交换机名称
     * @param routing_key[in]: 路由键
     * @param message[in]: 要推送的消息
     */
    bool publish(const std::string& exchange_name,
                 const std::string& routing_key, const std::string& message);

    /**
     * @brief 客户端向交换机订阅消费消息
     * @param queue_name[in]: 订阅的队列名称
     * @param consume_tag[in]: 消费标记
     * @param callback[in]: 收到消息的回到函数
     */
    bool consume(const std::string& queue_name, const std::string& consume_tag,
                 const MessageCallback_t callback);

   private:
    static void watcher_callback(struct ev_loop* loop, ev_async* watcher,
                                 int32_t revents);

   private:
    std::thread loop_thread_;
    struct ev_async async_watcher_;
    struct ev_loop* loop_ = nullptr;                         // I/O事件监控句柄
    std::shared_ptr<AMQP::LibEvHandler> handler_ = nullptr;  // libEvHandler句柄
    std::shared_ptr<AMQP::TcpConnection> connection_ = nullptr;  // 连接对象
    std::shared_ptr<AMQP::TcpChannel> channel_ = nullptr;        // 信道对象
};

using  MQClientPtr = std::shared_ptr<MQClient>;
}  // namespace chat

#endif  // COMMON_RABBITMQ_H